#include <QDomDocument>
#include <QDomElement>
#include <QFile>
#include <QTextStream>
#include <sstream>
#include "MapManager.hpp"
#include "TileCutter.hpp"

MapManager::MapManager()
{
}

//**************//
// Saving path  //
//**************//
void    MapManager::save(std::list<SpriteSheet*>& spriteSheets, Map &map, QString const& fileName)
{
    QDomDocument        document;
    std::stringstream   converter;


    QDomElement     rootElement = document.createElement("map");

    addToDom(document, rootElement, "name", map.getName());
    addToDom(document, rootElement, "width", map.getWidth());
    addToDom(document, rootElement, "height", map.getHeight());

    QDomElement     sheets = document.createElement("sprites_sheets");

    std::list<SpriteSheet*>::iterator   sheetsBegin = spriteSheets.begin();
    std::list<SpriteSheet*>::iterator   sheetsEnd = spriteSheets.end();

    while (sheetsBegin != sheetsEnd)
    {
        QDomElement     sheet = document.createElement("sprite_sheet");
        addToDom(document, sheet, "name", (*sheetsBegin)->getName());
        addToDom(document, sheet, "path", (*sheetsBegin)->getPath());
        addToDom(document, sheet, "width", (*sheetsBegin)->getWidth());
        addToDom(document, sheet, "height", (*sheetsBegin)->getHeight());
        sheets.appendChild(sheet);
        ++sheetsBegin;
    }
    rootElement.appendChild(sheets);

    QDomElement     tiles = document.createElement("tiles");

    std::list<Tile*>*           tilesList = map.getTiles();
    std::list<Tile*>::iterator  tilesBegin = tilesList->begin();
    std::list<Tile*>::iterator  tilesEnd = tilesList->end();

    while (tilesBegin != tilesEnd)
    {
        QDomElement     tile = document.createElement("tile");
        addToDom(document, tile, "sprite", (*tilesBegin)->getName());
        addToDom(document, tile, "position_x", (*tilesBegin)->getPositionX());
        addToDom(document, tile, "position_y", (*tilesBegin)->getPositionY());

        tiles.appendChild(tile);
        ++tilesBegin;
    }
    rootElement.appendChild(tiles);

    document.appendChild(rootElement);
    writeToFile(document, fileName);
}

void                MapManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, QString const& value)
{
    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(value));
    element.appendChild(child);
}

void                MapManager::addToDom(QDomDocument& document, QDomElement& element, QString const& name, int value)
{
    std::stringstream   converter;

    converter << value;

    QDomElement     child = document.createElement(name);
    child.appendChild(document.createTextNode(converter.str().data()));
    element.appendChild(child);
}

void                MapManager::writeToFile(QDomDocument& document, QString const& fileName)
{
    QDomNode        xmlNode = document.createProcessingInstruction("xml","version=\"1.0\" encoding=\"UTF-8\"");
    QFile           file(fileName);
    QTextStream     out;

    if (file.open(QIODevice::WriteOnly) == false)
    {
        throw std::exception();
    }
    document.insertBefore(xmlNode, document.firstChild());
    out.setDevice(&file);
    document.save(out, 2);
    file.close();
}

//**************//
// Loading path //
//**************//
Map*    MapManager::load(QString const& fileName, std::list<SpriteSheet*>& sheetsDictionary)
{
    QDomDocument    document;

    buildDomFromFile(document, fileName);

    Map*            newMap = new Map();
    QDomElement     rootElement = document.documentElement();
    QDomElement     child = rootElement.firstChild().toElement();

    while (child.isNull() == false)
    {
        if (child.tagName() == "name")
            newMap->setName(child.text());
        else if (child.tagName() == "width")
            newMap->setWidth(child.text().toInt());
        else if (child.tagName() == "height")
            newMap->setHeight(child.text().toInt());
        else if (child.tagName() == "sprites_sheets")
            loadSheets(child, sheetsDictionary);
        else if (child.tagName() == "tiles")
            loadTiles(child, newMap, sheetsDictionary);
        child = child.nextSibling().toElement();
    }
    return newMap;
}

void    MapManager::loadTiles(QDomElement& child, Map* newMap, std::list<SpriteSheet*>& sheetsDictionary)
{
    QDomElement tiles = child.firstChild().toElement();

    while (tiles.isNull() == false)
    {
        Tile*           newTile = new Tile();
        QDomElement     tile = tiles.firstChild().toElement();

        while (tile.isNull() == false)
        {
            if (tile.tagName() == "sprite")
                newTile->setName(tile.text());
            else if (tile.tagName() == "position_x")
                newTile->setPositionX(tile.text().toInt());
            else if (tile.tagName() == "position_y")
                newTile->setPositionY(tile.text().toInt());

            tile = tile.nextSibling().toElement();
        }

        std::list<SpriteSheet*>::iterator         mainListBegin = sheetsDictionary.begin();
        const std::list<SpriteSheet*>::iterator   mainListEnd = sheetsDictionary.end();

        QPixmap*     image;
        while (mainListBegin != mainListEnd)
        {
            image = (*mainListBegin)->getImageFromName(newTile->getName());
            if (image != NULL)
            {
                newTile->setPixmap(image);
                break;
            }
            ++mainListBegin;
        }

        newMap->addTile(newTile);
        tiles = tiles.nextSibling().toElement();
    }
}

void    MapManager::loadSheets(QDomElement& child, std::list<SpriteSheet*>& sheetsDictionary)
{
    TileCutter  cutter(sheetsDictionary);
    QDomElement sheets = child.firstChild().toElement();
    QString     name, path;
    int         width, height;

    while (sheets.isNull() == false)
    {
        QDomElement     sheet = sheets.firstChild().toElement();


        while (sheet.isNull() == false)
        {
            if (sheet.tagName() == "name")
                name = sheet.text();
            else if (sheet.tagName() == "path")
                path = sheet.text();
            else if (sheet.tagName() == "width")
                width = sheet.text().toInt();
            else if (sheet.tagName() == "height")
                height = sheet.text().toInt();

            sheet = sheet.nextSibling().toElement();
        }
        if (SpriteSheet::alreadyLoaded(sheetsDictionary, name) == false)
        {
            cutter.cutSprite(name, path, width, height);
        }
        sheets = sheets.nextSibling().toElement();
    }
}

void    MapManager::buildDomFromFile(QDomDocument& document, QString const& fileName)
{
    QFile   file(fileName);

    if (file.open(QIODevice::ReadOnly) == false)
    {
        throw std::exception();
    }

    QString error;
    int     errorLine, errorColumn;

    document.setContent(&file, true, &error, &errorLine, &errorColumn);
    file.close();
}
